Skip to content

refactor(billing): require wallet address in public wallet API responses - #3521

Open
baktun14 wants to merge 5 commits into
mainfrom
refactor/billing-require-wallet-address-in-api
Open

refactor(billing): require wallet address in public wallet API responses#3521
baktun14 wants to merge 5 commits into
mainfrom
refactor/billing-require-wallet-address-in-api

Conversation

@baktun14

@baktun14 baktun14 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Why

Closes CON-745

CON-744 (#3512, #3513, deployed) guarantees that every wallet returned by the public API has an address: GET /v1/wallets only returns activated wallets (activation happens strictly after address derivation), and POST /v1/start-trial responds only after the address is derived. The loose nullable contract now only hides bugs and forces null-tolerant handling in API consumers.

What

apps/api — declare address (plus id, userId, createdAt, which are DB-non-null already) as required in the wallet response schema:

  • WalletOutputSchema: drop .nullable() on the four fields; createdAt switches z.coerce.date()z.date() because coercion makes the OpenAPI generator's isNullable() probe succeed on null (new Date(null) is valid), which kept a phantom nullable: true in the spec. Response schemas are compile-time/docs only — nothing parses them at runtime.
  • The invariant is proven at compile time, with no new runtime asserts: toPublic now requires WalletInitialized (moved to the repository from wallet-reader.service.ts), WalletReaderService.getWallets filters with a type predicate (which also silently excludes any pathological activated-but-addressless legacy row instead of 500ing), and WalletInitializerService.#ensureWalletVia returns the narrowed type its JSDoc already promised.
  • New createInitializedUserWallet seeder for fixtures that mock the narrowed contract.

Generated artifacts — docs snapshot, swagger/openapi.json, and @akashnetwork/console-api-types regenerated. Note: the checked-in spec was stale (predates #3513 and the idempotent top-up work), so the regen also drops the long-removed 202 from /v1/start-trial, adds its 409, and picks up idempotencyKey on checkout — incidental drift, not introduced by this PR.

apps/deploy-web — drop the null-address tolerance that CON-744 shipped as a temporary bridge:

Verified end-to-end against a locally booted API: an activated wallet returns a non-null address, a pre-activation wallet is excluded from GET /v1/wallets, and the served OpenAPI doc marks all four fields required (only the intentionally nullable 3DS fields remain nullable).

Summary by CodeRabbit

  • New Features
    • Added optional idempotencyKey support for Stripe transaction confirmation requests.
    • Added a 409 response for /v1/start-trial when trial provisioning is already in progress.
  • Bug Fixes
    • Updated wallet readiness/initialization so wallet-dependent steps wait for a fully initialized wallet with a definite address.
    • Wallet-related responses now return non-null id, userId, address, and createdAt where applicable.
  • Improvements
    • Improved usage history query handling for date ranges by materializing endDate and deriving startDate when needed.
  • Tests
    • Refreshed wallet seeding and readiness tests to match the tightened wallet initialization behavior.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f4fd10be-7bdb-410c-8630-fb377207d2e9

📥 Commits

Reviewing files that changed from the base of the PR and between 4adc9f9 and 9d75988.

⛔ Files ignored due to path filters (1)
  • apps/api/test/functional/__snapshots__/docs.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (3)
  • apps/api/src/billing/http-schemas/usage.schema.ts
  • apps/api/src/billing/routes/usage/usage.router.ts
  • apps/api/swagger/openapi.json
💤 Files with no reviewable changes (1)
  • apps/api/swagger/openapi.json

📝 Walkthrough

Walkthrough

Wallet initialization now guarantees initialized public wallet data, frontend hooks handle wallet presence independently of address presence, usage history dates are materialized during parsing, and API schemas document updated wallet, trial, and idempotency contracts.

Changes

Wallet initialization and API contract

Layer / File(s) Summary
Backend wallet initialization flow
apps/api/src/billing/..., apps/api/src/deployment/...
Introduces the repository-owned WalletInitialized type, normalizes wallet addresses during initialization, filters uninitialized wallets, and updates dependent imports.
Initialized wallet fixtures and refill coverage
apps/api/test/seeders/..., apps/api/src/billing/services/refill/...
Adds an initialized-wallet seeder and uses it in refill scenarios requiring wallet activation state.
Frontend wallet readiness and persistence
apps/deploy-web/src/hooks/...
Treats an existing wallet as ready and persists it without requiring wallet.address; related tests and helpers now use non-null addresses.
Wallet and trial API contracts
apps/api/src/billing/http-schemas/..., apps/api/swagger/openapi.json, packages/console-api-types/...
Makes wallet response fields non-nullable, replaces the start-trial 202 response with 409, and adds the optional idempotencyKey field alongside specification metadata updates.
Usage history date normalization
apps/api/src/billing/http-schemas/usage.schema.ts, apps/api/src/billing/routes/usage/usage.router.ts, apps/api/swagger/openapi.json
Derives missing usage-history dates during schema transformation, validates the resulting range, and passes the materialized dates to controllers.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: ygrishajev

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/billing-require-wallet-address-in-api

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.22%. Comparing base (627ad0c) to head (9d75988).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3521      +/-   ##
==========================================
- Coverage   74.18%   73.22%   -0.97%     
==========================================
  Files        1142     1052      -90     
  Lines       29721    27371    -2350     
  Branches     7447     6979     -468     
==========================================
- Hits        22049    20042    -2007     
+ Misses       6771     6459     -312     
+ Partials      901      870      -31     
Flag Coverage Δ *Carryforward flag
api 87.45% <100.00%> (+0.04%) ⬆️
deploy-web 63.67% <100.00%> (-0.01%) ⬇️
log-collector ?
notifications 93.84% <ø> (ø) Carriedforward from 4adc9f9
provider-console 81.38% <ø> (ø) Carriedforward from 4adc9f9
provider-inventory ?
provider-proxy 88.17% <ø> (ø) Carriedforward from 4adc9f9
tx-signer ?

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
...repositories/user-wallet/user-wallet.repository.ts 81.35% <100.00%> (ø)
...s/wallet-initializer/wallet-initializer.service.ts 100.00% <100.00%> (ø)
...ng/services/wallet-reader/wallet-reader.service.ts 94.73% <100.00%> (+0.61%) ⬆️
...ces/deployment-reader/deployment-reader.service.ts 85.41% <ø> (ø)
...ces/deployment-writer/deployment-writer.service.ts 100.00% <ø> (ø)
apps/deploy-web/src/hooks/useEnsureTrialStarted.ts 100.00% <100.00%> (ø)
apps/deploy-web/src/hooks/useManagedWallet.ts 92.59% <100.00%> (-0.96%) ⬇️

... and 91 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts (1)

6-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use type-only imports for wallet aliases.

UserWalletOutput, UserWalletPublicOutput, and WalletInitialized are type-only symbols, while UserWalletRepository is a runtime dependency. Split or annotate these imports to satisfy the shared TypeScript import rule and avoid runtime import issues.

  • apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts#L6-L6: mark the wallet aliases as type-only while keeping UserWalletRepository as a value import.
  • apps/api/src/billing/services/wallet-reader/wallet-reader.service.ts#L6-L6: apply the same type-only import treatment.

As per coding guidelines, TypeScript imports must use consistent type imports.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts`
at line 6, Update the imports in
apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts#L6-L6
and apps/api/src/billing/services/wallet-reader/wallet-reader.service.ts#L6-L6
so UserWalletOutput, UserWalletPublicOutput, and WalletInitialized use type-only
imports, while UserWalletRepository remains a runtime import.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts`:
- Around line 87-94: Update `#ensureWalletVia` to perform the wallet address
mutation through the authorization-scoped repository argument by replacing
this.userWalletRepository.updateById with repository.updateById, while
preserving the existing update parameters and returned wallet behavior.

In `@apps/api/src/billing/services/wallet-reader/wallet-reader.service.ts`:
- Around line 22-24: Update the wallet filter in the wallet-reader mapping flow
to validate that address is present and non-empty before asserting
WalletInitialized; reject undefined, null, and empty or whitespace-only values
while preserving the activatedAt requirement and existing toPublic mapping.

In
`@apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts`:
- Around line 19-20: Change the WalletInitialized import in the deployment
reader service to a type-only import, while keeping WalletReaderService as a
regular import.

In `@apps/api/swagger/openapi.json`:
- Line 1790: Remove the generated static default value for dynamic endDate at
apps/api/swagger/openapi.json lines 1790-1790 and 1916-1916, omitting the
default property entirely at both sites so the OpenAPI schema does not contain a
date that becomes stale.
- Line 5: Update the OpenAPI server URL in the generator configuration to the
intended public API URL instead of localhost, then regenerate the specification
so Swagger UI and generated clients use the deployed server.

---

Nitpick comments:
In
`@apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts`:
- Line 6: Update the imports in
apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts#L6-L6
and apps/api/src/billing/services/wallet-reader/wallet-reader.service.ts#L6-L6
so UserWalletOutput, UserWalletPublicOutput, and WalletInitialized use type-only
imports, while UserWalletRepository remains a runtime import.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cee7dad8-2ed7-4893-a71b-d7722cb62820

📥 Commits

Reviewing files that changed from the base of the PR and between 627ad0c and cb04b6c.

⛔ Files ignored due to path filters (1)
  • apps/api/test/functional/__snapshots__/docs.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (17)
  • apps/api/src/billing/http-schemas/wallet.schema.ts
  • apps/api/src/billing/repositories/user-wallet/user-wallet.repository.ts
  • apps/api/src/billing/services/refill/refill.service.spec.ts
  • apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts
  • apps/api/src/billing/services/wallet-reader/wallet-reader.service.ts
  • apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts
  • apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts
  • apps/api/src/deployment/services/deployment-writer/deployment-writer.service.spec.ts
  • apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts
  • apps/api/src/deployment/services/lease/lease.service.spec.ts
  • apps/api/swagger/openapi.json
  • apps/api/test/seeders/user-wallet.seeder.ts
  • apps/deploy-web/src/hooks/useEnsureTrialStarted.spec.ts
  • apps/deploy-web/src/hooks/useEnsureTrialStarted.ts
  • apps/deploy-web/src/hooks/useManagedWallet.spec.tsx
  • apps/deploy-web/src/hooks/useManagedWallet.ts
  • packages/console-api-types/src/schema.d.ts
💤 Files with no reviewable changes (1)
  • apps/deploy-web/src/hooks/useEnsureTrialStarted.spec.ts

Comment thread apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts Outdated
Comment thread apps/api/swagger/openapi.json Outdated
Comment thread apps/api/swagger/openapi.json Outdated
baktun14 added 3 commits July 24, 2026 23:04
- merge duplicated @src/billing/repositories import in deployment-writer
- flatten managed-wallet storage-sync effect to an early return + ternary
- revert accidental openapi regeneration artifacts (localhost server url,
  today's date defaults, dropped trailing newline)
… guard

Addresses review feedback on the wallet-address contract:

- #ensureWalletVia now mutates through the CASL-scoped `repository`
  argument instead of the unscoped `this.userWalletRepository`, so the
  get/check/mutate sequence stays authorization-scoped during trial init.
- getWallets filters activated wallets with a truthiness check on
  `address` so an empty-string address is also excluded and the
  `WalletInitialized` predicate holds.
- Use type-only imports for the wallet type aliases and WalletInitialized.
…enAPI spec

The usage-history query schema defaulted `endDate` with
`.default(() => new Date()...)`. The OpenAPI generator evaluates that at
generation time and freezes a static date into the spec, which then goes
stale and misleads generated clients into sending an old default date.

Move the "default to today" logic into the schema transform (next to the
existing startDate-from-endDate derivation) so runtime behavior is
unchanged but the generated spec no longer carries a static default.
Regenerate openapi.json, update the docs snapshot, and drop the
now-redundant non-null assertions in the router and refine.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant